package controllers

import (
	"container/list"
	"encoding/json"
	"github.com/astaxie/beego/orm"
	"github.com/googollee/go-socket.io"
	"net/http"
	"strconv"
	"sync"
	//"time"
)

type SoctetioController struct {
	OnlineController
}

// 全局数据定义----------------------------------------------------------------------------------
type Msg struct {
	Id        string `json:"id"`         // 消息ID
	SrcId     string `json:"src_id"`     // 仅在已回复消息中有意义,指原消息id
	SenderAcc string `json:"sender_acc"` // 发送人账号
	RecverAcc string `json:"recver_acc"` // 接收人账号
	Content   string `json:"content"`    // 内容
	Unix      string `json:unix`         // 发送时间戳
}

type LiveBucket struct {
	Usolock    sync.RWMutex
	UserSock   map[string]*socketio.Socket // key:用户短连接SessionId,value:用户连接的sock
	LiveRecord map[int]*list.List          // 消息记录,key:1-客户提问,2-讲师回复,3-讲师直播记录
	LecAcc     string                      // 讲师账号
}

var (
	gloLivebuk LiveBucket
)

func (this *LiveBucket) init() {
	// 初始化sock存储器
	this.UserSock = make(map[string]*socketio.Socket)

	// 消息记录初始化
	this.LiveRecord = make(map[int]*list.List)
	for key := 1; key < 4; key++ {
		if ls, ok := this.LiveRecord[key]; !ok {
			ls = new(list.List)
			this.LiveRecord[key] = ls
		}
	}
	return
}

func (this *LiveBucket) InsertMsgToDb(msg *Msg, flag int) (err error) {
	db := orm.NewOrm()
	sql := ps("insert into `live_record` (src_id,sender_acc,sender_flag,recver_acc,content,unix) values ('%v','%v','%v','%v','%v','%v');", msg.SrcId, msg.SenderAcc, flag, msg.RecverAcc, msg.Content, msg.Unix)
	_, err = db.Raw(sql).Exec()
	if err != nil {
		return
	}

	sql = ps("select max(id) as id from `live_record`;")
	var res []orm.Params
	_, err = db.Raw(sql).Values(&res)
	if err != nil {
		return
	}
	if len(res) > 0 && res[0]["id"] != nil {
		msg.Id = res[0]["id"].(string)
	}
	return
}

func (this *LiveBucket) AddMsg(msg *Msg) (bRes bool) {
	// 消息处理
	msg.Unix = ps("%v", TimeNow)

	usr, ok := UserSessions.QueryloginA(msg.SenderAcc)
	if ok { // 当前用户在线
		// 消息入库
		err := this.InsertMsgToDb(msg, usr.Flag)
		if err != nil {
			log("消息入库失败:%s", err.Error())
			return
		}

		this.Usolock.Lock()
		defer this.Usolock.Unlock()

		if usr.Flag == 1 { // 讲师
			if msg.SrcId == "" { // 讲师直播内容
				if this.LiveRecord[3].Len() > 2000 {
					this.LiveRecord[3].Remove(this.LiveRecord[3].Front())
				}
				this.LiveRecord[3].PushBack(msg)
			} else { // 讲师回复客户提问
				this.DelMsg(1, msg.Id)
				if this.LiveRecord[2].Len() > 2000 {
					this.LiveRecord[2].Remove(this.LiveRecord[3].Front())
				}
				this.LiveRecord[2].PushBack(msg)
			}
		} else { // 客户提问
			msg.RecverAcc = this.LecAcc
			if this.LiveRecord[1].Len() > 2000 {
				this.LiveRecord[1].Remove(this.LiveRecord[3].Front())
			}
			this.LiveRecord[1].PushBack(msg)
		}
		bRes = true
	}
	return
}

func (this *LiveBucket) DelMsg(key int, msgid string) {
	this.Usolock.Lock()
	defer this.Usolock.Unlock()
	if ls, ok := this.LiveRecord[key]; ok {
		var msg Msg
		for e := ls.Front(); e != nil; e = e.Next() {
			msg = e.Value.(Msg)
			if msgid == msg.Id {
				ls.Remove(e)
				break
			}
		}
	}
	return
}

// 存储用户连接对象
func (this *LiveBucket) AddSock(sid string, so *socketio.Socket) (bRes bool, acc string) {
	bRes = false

	this.Usolock.Lock()
	defer this.Usolock.Unlock()

	usr, ok := UserSessions.QueryloginS(sid)
	if ok { // 当前用户在线
		if _, ok := this.UserSock[usr.Account]; ok {
			delete(this.UserSock, usr.Account)
		}
		this.UserSock[usr.Account] = so
		if usr.Flag == 1 { // 记录讲师账号
			this.LecAcc = usr.Account
			this.TeacherIoNotify("1")
		}
		acc = usr.Account
		bRes = true
	}

	return
}

// 客户端断开处理
func (this *LiveBucket) DisconnSock(so *socketio.Socket) {
	this.Usolock.Lock()
	defer this.Usolock.Unlock()

	for key, val := range this.UserSock {
		if val == so {
			delete(this.UserSock, key)

			// 如果是讲师断开,通知直播间其他人
			if key == this.LecAcc {
				this.TeacherIoNotify("0")
			}
		}
	}
	return
}

// 讲师进出直播间消息
func (this *LiveBucket) TeacherIoNotify(status string) {
	var err error
	for _, so := range this.UserSock {
		err = (*so).Emit("teacher", status)
		if err != nil {
			log("emit err: %v", err)
		}
	}

	return
}

func (this *LiveBucket) SendMsg(msg *Msg) {
	_, ok := UserSessions.QueryloginA(msg.RecverAcc)
	if ok { // 当前用户在线
		this.Usolock.Lock()
		defer this.Usolock.Unlock()
		json_byte, err := json.Marshal(*msg)
		if err != nil {
			log("json marshal err: %v", err)
			return
		}

		// 推送消息
		if msg.RecverAcc == "" && msg.SenderAcc == this.LecAcc { // 讲师直播
			for _, so := range this.UserSock {
				err = (*so).Emit("msg", string(json_byte))
				if err != nil {
					log("emit err: %v", err)
				}
			}
		} else {
			if so, ok := this.UserSock[msg.RecverAcc]; ok {
				err = (*so).Emit("msg", string(json_byte))
				if err != nil {
					log("emit err: %v", err)
				}
			} else {
				log("[%s]未连接直播间...", msg.RecverAcc)
			}

			// 消息反馈给自己
			if so, ok := this.UserSock[msg.SenderAcc]; ok {
				err = (*so).Emit("msg", string(json_byte))
				if err != nil {
					log("emit err: %v", err)
				}
			}
		}

	} else {
		log("接收人[%s]未登录......", msg.RecverAcc)
	}

	return
}

// sid,flag(1-未回复问题,2-已回复问题,3-讲师直播记录),begidx,counts(多少条,按时间最近返回)
func (this *SoctetioController) LiveRecordQuery() {
	flag, _ := this.GetInt("flag")
	counts, _ := this.GetInt("counts")
	begidx, _ := this.GetInt("begidx")

	if !CheckArg(flag) {
		this.Rec = &Recv{5, "flag不能为空", nil}
		return
	}

	db := orm.NewOrm()
	var res []orm.Params
	sql := "select * from `live_record`"
	sqlc := "select count(id) as num from `live_record`"
	switch flag {
	case 1:
		sql += " where src_id=0 and sender_flag=0"
		sqlc += " where src_id=0 and sender_flag=0"
		if this.User.Flag == 0 { // 前端用户只查询自己的
			sql += ps(" and sender_acc='%s'", this.User.Account)
			sqlc += ps(" and sender_acc='%s'", this.User.Account)
		}
	case 2: // 已回复提问的回复
		sql += " where src_id>0"
		sqlc += " where src_id>0"
		if this.User.Flag == 0 { // 前端用户只查询自己的
			sql += ps(" and recver_acc='%s'", this.User.Account)
			sqlc += ps(" and recver_acc='%s'", this.User.Account)
		}
	case 3:
		sql += " where sender_flag=1 and src_id=0"
		sqlc += " where sender_flag=1 and src_id=0"
	}
	sql += ps(" order by unix desc limit %d,%d;", begidx, counts)

	_, err := db.Raw(sqlc).Values(&res)
	if err != nil {
		log("查询历史记录失败:%s", err.Error())
		this.Rec = &Recv{5, "查询失败", nil}
		return
	}
	var strnum string = ""
	if res[0]["num"] != nil {
		strnum = res[0]["num"].(string)
	}

	_, err = db.Raw(sql).Values(&res)
	if err != nil {
		log("查询历史记录失败:%s", err.Error())
		this.Rec = &Recv{5, "查询失败", nil}
		return
	}
	if flag == 2 { // 已回复问题
		for idx := range res {
			item := res[idx]
			src_id, _ := strconv.Atoi(item["src_id"].(string))
			sql = ps("select content,unix from `live_record` where id=%d;", src_id)
			var tres []orm.Params
			_, err := db.Raw(sql).Values(&tres)
			if err != nil {
				log("查询提问内容失败:%s", err.Error())
				this.Rec = &Recv{5, "查询失败", nil}
				return
			}
			if len(tres) > 0 {
				item["src_content"] = tres[0]["content"].(string)
				item["src_unix"] = tres[0]["unix"].(string)
			} else {
				item["src_content"] = ""
				item["src_unix"] = "0"
			}

		}
	}

	type RecvEx struct {
		Total  string
		Detail interface{}
	}
	this.Rec = &Recv{3, "查询成功", &RecvEx{strnum, res}}
	return
}

// 全局数据定义----------------------------------------------------------------------------------

func init() {
	gloLivebuk.init()

	go SocketioSvr()
	return
}

func SocketioSvr() {
	server, err := socketio.NewServer(nil)
	if err != nil {
		log("create socketio err:%v", err)
	}

	err = server.On("connection", OnSocketioConnection)
	if err != nil {
		log("on connection event failed: %s", err.Error())
		return
	}

	err = server.On("error", func(so socketio.Socket, err error) {
		log("get error info: %s", err.Error())

	})
	if err != nil {
		log("on error event failed: %s", err)
		return
	}

	http.HandleFunc("/socket.io/", func(w http.ResponseWriter, r *http.Request) {
		origin := r.Header.Get("Origin")
		//log("origin: %s", origin)
		w.Header().Set("Access-Control-Allow-Origin", origin)
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		server.ServeHTTP(w, r)
	})
	//http.Handle("/socket.io/", server)
	http.Handle("/", http.FileServer(http.Dir("./asset")))
	log("Socketio Serving at %s", conf("so_port"))
	err = http.ListenAndServe(ps(":%s", conf("so_port")), nil)
	if err != nil {
		log("listen and server err:%s", err.Error())
		return
	}
	log("Socketio Serving Exit......")
	return
}

func OnSocketioConnection(so socketio.Socket) {
	//var req *http.Request = so.Request()
	//sid := req.FormValue("SessionId")
	//log("开始长连接................")

	so.On("verify", func(sid string) {
		bRes, acc := gloLivebuk.AddSock(sid, &so)
		if bRes {
			so.Join("chat")
			so.On("msg", OnSocketioMsg)

			// 验证结果
			var str string = ""
			if gloLivebuk.LecAcc != "" {
				if _, ok := UserSessions.QueryloginA(gloLivebuk.LecAcc); ok {
					str = `{"status":"1","live_status":"1"}`
				} else {
					str = `{"status":"1","live_status":"0"}`
				}
			} else {
				str = `{"status":"1","live_status":"0"}`
			}
			err := so.Emit("verify", str)
			if err != nil {
				log("emit verify result err: %s", err.Error())
			}
			log("[%s]长连接验证成功................", acc)

			// 客户端断开长连接
			so.On("disconnection", func() {
				log("on disconnect in...")
				gloLivebuk.DisconnSock(&so)
			})
		} else {
			//time.Sleep(time.Second * 1) // 携程休眠是为了保证客户端已经监控消息
			err := so.Emit("verify", `{"status":"0","live_status":"0"}`)
			if err != nil {
				log("emit verify result err: %s", err.Error())
			}

			so.Disconnect()
		}
	})

	so.On("disconnection", func() {
		log("on disconnect out")
	})

	return
}

func OnSocketioMsg(msg string) {
	log("on msg: %s", msg)

	var data Msg
	err := json.Unmarshal([]byte(msg), &data)
	if err != nil {
		log("解析json错误:%s", err.Error())
	} else {
		// 信息添加
		bRes := gloLivebuk.AddMsg(&data)

		if bRes == true {
			// 信息发送
			gloLivebuk.SendMsg(&data)
		}
	}

	return
}
